{-# LANGUAGE RecordWildCards #-}

module Strategy.Ruby.BundleShow (
  analyze',
  BundleShowDep (..),
  buildGraph,
  bundleShowParser,
) where

import Control.Effect.Diagnostics
import Control.Monad (void)
import Data.Map.Strict qualified as Map
import Data.Text (Text)
import Data.Void (Void)
import DepTypes
import Effect.Exec
import Graphing (Graphing)
import Graphing qualified
import Path
import Text.Megaparsec
import Text.Megaparsec.Char

bundleShowCmd :: Command
bundleShowCmd =
  Command
    { cmdName = "bundle"
    , cmdArgs = ["show"]
    , cmdAllowErr = Never
    }

analyze' :: (Has Exec sig m, Has Diagnostics sig m) => Path Abs Dir -> m (Graphing Dependency)
analyze' dir = do
  deps <- execParser @[BundleShowDep] bundleShowParser dir bundleShowCmd
  context "Building dependency graph" $ pure (buildGraph deps)

buildGraph :: [BundleShowDep] -> Graphing Dependency
buildGraph = Graphing.fromList . map toDependency
  where
    toDependency BundleShowDep{..} =
      Dependency
        { dependencyType = GemType
        , dependencyName = depName
        , dependencyVersion = Just (CEq depVersion)
        , dependencyLocations = []
        , dependencyEnvironments = mempty
        , dependencyTags = Map.empty
        }

data BundleShowDep = BundleShowDep
  { depName :: Text
  , depVersion :: Text
  }
  deriving (Eq, Ord, Show)

type Parser = Parsec Void Text

bundleShowParser :: Parser [BundleShowDep]
bundleShowParser = concat <$> ((line <|> ignoredLine) `sepBy` eol) <* eof
  where
    isEndLine :: Char -> Bool
    isEndLine '\n' = True
    isEndLine '\r' = True
    isEndLine _ = False

    -- ignore content until the end of the line
    ignored :: Parser ()
    ignored = void $ takeWhileP (Just "ignored") (not . isEndLine)

    ignoredLine :: Parser [BundleShowDep]
    ignoredLine = do
      ignored
      pure []

    findDep :: Parser Text
    findDep = takeWhileP (Just "dep") (/= ' ')

    findVersion :: Parser Text
    findVersion = takeWhileP (Just "version") (/= ')')

    line :: Parser [BundleShowDep]
    line = do
      _ <- chunk "  * "
      dep <- findDep
      _ <- chunk " ("
      version <- findVersion
      _ <- char ')'
      pure [BundleShowDep dep version]
